
# $Id: SQConfig.pm 149 2013-04-13 05:42:04Z chengxin.cai $

use strict;

package SQConfig;

use Switch;
use Carp;
use Data::Dumper;

use lib '/home/caiche/Programs/Perl';
use PerlUtil qw( gen_getter gen_setter );

use SQConfig::Node;
use SQConfig::Enclosure;
use SQConfig::Volume;
use SQConfig::Overflow;
use SQConfig::FloatingIp;
use SQConfig::Bdr;

sub import {
    my $package = shift;

    ##export the constructor
    no strict 'refs';
    my ($caller) = caller;
    *{ $caller . '::' . $package } = \&{$package};

    ##install all the getters and setters
    gen_getter( __PACKAGE__, 'nodes');
    gen_getter( __PACKAGE__, 'volumes' );
    gen_getter( __PACKAGE__, 'encs' );
    gen_getter( __PACKAGE__, 'overflow' );
    gen_getter( __PACKAGE__, 'floating_ip' );
    gen_getter( __PACKAGE__, 'bdr' );

    gen_setter( __PACKAGE__, 'add_node' );
    gen_setter( __PACKAGE__, 'add_volume' );
    gen_setter( __PACKAGE__, 'add_enc' );
    gen_setter( __PACKAGE__, 'set_overflow' );
    gen_setter( __PACKAGE__, 'set_floating_ip' );
    gen_setter( __PACKAGE__, 'set_bdr' );
}

# constructor
sub SQConfig {
    my @nodelist;
    my @enclist;
    my @volumelist;

    my $overflow;
    my $floating_ip;
    my $bdr;

    my $get_node = sub { my $nodeid = shift; $nodelist[$nodeid]; };

    bless sub {
        my $op = shift;

        switch ($op) {
            case 'nodes'           { return @nodelist; }
            case 'volumes'         { return @volumelist; }
            case 'encs'            { return @enclist; }
            case 'overflow'        { return $overflow; }
            case 'floating_ip'     { return $floating_ip; }
            case 'bdr'             { return $bdr; }
            case 'get_node'        { return $get_node; }
            case 'reset'           { @nodelist    = ();
                                     @enclist     = ();
                                     @volumelist  = ();
                                     $overflow    = undef;
                                     $floating_ip = undef;
                                     $bdr         = undef;
                                   }
            case 'add_node'        { return sub { push @nodelist, shift; } }
            case 'add_volume'      { return sub { push @volumelist, shift; } }
            case 'add_enc'         { return sub { push @enclist, shift; } }
            case 'set_overflow'    { return sub { $overflow = shift; } }
            case 'set_floating_ip' { return sub { $floating_ip = shift; } }
            case 'set_bdr'         { return sub { $bdr = shift; } }
            else                   { croak "Invalid operation $op"; }
        }
    }, __PACKAGE__;
}

####Utilities

sub reset {
    my $self = shift;
    &{$self}('reset');
}

sub get_node {
    my ( $self, $nodeid ) = @_;
    &{&{$self}('get_node')}($nodeid);
}

sub read {
    my ($self, $filename) = @_;

    croak "filename not provided for SQConfig::read" if ( ! $filename );
    croak "Unable to find file $filename" if ( ! -f $filename );

    my $__parse_nodes = sub {
        my ( $self, $fh ) = @_;

        while (my $line = <$fh>) {

            return if($line =~ /^end node/);
            next if ($line =~ /^#/ || $line =~ /^\s*$/);

            chomp $line;
            $self->add_node( Node($line) );
        }

        croak "Syntax error in sqconfig (missing end node), "
            . " processing cannot continue";
    };

    my $__parse_enclosure = sub {
        my ( $self, $fh ) = @_;

        while (my $line = <$fh>) {

            return if($line =~ /^end enclosure/);
            next if ($line =~ /^#/ || $line =~ /^\s*$/);

            chomp $line;
            $self->add_enc( Enclosure($line) );
        }

        croak "Syntax error in sqconfig (missing end enclosure), "
            . " processing cannot continue";
    };

    my $__parse_volumes = sub {
        my ( $self, $fh ) = @_;

        while (my $line = <$fh>) {
            return if ($line =~ /^end [tma]+se/);
            next if ($line =~/^#/ || $line =~/^\s*$/);

            chomp $line;
            my $volume = Volume($line);
            my $node = $self->get_node( $volume->nodeid() );

            $volume->set_cabno( $node->cabno() );

            $self->add_volume($volume);
        }

        # Shouldn't get here - badly formed sqconfig (missing end [tma]se)
        croak "Syntax error in sqconfig (missing end [tma]se), "
            . "processing cannot continue";
    };

    my $__parse_overflow = sub {
        my ( $self, $fh ) = @_;

        my $overflow = Overflow();
        $self->set_overflow($overflow);

        while (my $line = <$fh>) {
            return if ($line =~ /^end overflow/);
            next if ($line =~/^#/ || $line =~/^\s*$/); # Comment or empty lines

            chomp $line;
            $overflow->add_dir($line);
        }

        # Shouldn't get here - badly formed sqconfig (missing end [tma]se)
        croak "Syntax error in sqconfig (missing end overflow), "
            . "processing cannot continue";
    };

    my $__parse_floating_ip = sub {
        my ( $self, $fh ) = @_;

        my $floating_ip = FloatingIp();
        $self->set_floating_ip($floating_ip);

        while (my $line = <$fh>) {
            return if ($line =~ /^end floating_ip/);
            next if ($line =~/^#/ || $line =~/^\s*$/); # Comment or empty lines

            chomp $line;
            $floating_ip->add_line($line);
        }

        # Shouldn't get here - badly formed sqconfig (missing end [tma]se)
        croak "Syntax error in sqconfig (missing end floating_ip), "
            . "processing cannot continue";
    };

    my $__parse_bdr = sub {
        my ( $self, $fh ) = @_;

        my $bdr = Bdr();
        $self->set_bdr($bdr);

        while (my $line = <$fh>) {
            return if ($line =~ /^end bdr/);
            next if ($line =~/^#/ || $line =~/^\s*$/); # Comment or empty lines

            chomp $line;
            $bdr->add_line($line);
        }

        # Shouldn't get here - badly formed sqconfig (missing end [tma]se)
        croak "Syntax error in sqconfig (missing end bdr), "
            . "processing cannot continue";
    };

    open my $fh, $filename or croak "Error opening $filename: $!";

    while (<$fh>) {
        $self->$__parse_nodes($fh) if (/^begin node/);
        $self->$__parse_enclosure($fh) if (/^begin enclosure/);
        $self->$__parse_volumes($fh) if (/^begin [tma]+se/);
        $self->$__parse_overflow($fh) if (/^begin overflow/);
        $self->$__parse_floating_ip($fh) if (/^begin floating_ip/);
        $self->$__parse_bdr($fh) if (/^begin bdr/);
    }

    $fh->close();
    return 1;
}

sub write {
    my ($self, $filename) = @_;

    croak "filename not provided for SQConfig::write" if ( ! $filename );

    my $__write_nodes = sub {
        my $self = shift;

        print "begin node\n";
        for my $node ( $self->nodes() ) {
            print $node . "\n";
        }
        print "end node\n\n";
    };

    my $__write_enclosures = sub {
        my $self = shift;

        print "begin enclosure\n";
        for my $enc ( $self->encs() ) {
            print $enc . "\n";
        }
        print "end enclosure\n\n";
    };

    my $__write_volumes = sub {
        my ( $self, $voltype ) = @_;

        for my $volume ( grep { $_->name() =~ /$voltype/ } $self->volumes() ) {
            print $volume . "\n";
        }
    };

    my $__write_tmse = sub {
        my $self = shift;

        print "begin tmase\n";
        $self->$__write_volumes('TLOG');
        print "end tmase\n\n";
    };

    my $__write_ase = sub {
        my $self = shift;

        print "begin ase\n";
        $self->$__write_volumes('AU');
        print "end ase\n\n";
    };

    my $__write_tse = sub {
        my $self = shift;

        print "begin tse\n";
        $self->$__write_volumes('DB');
        print "end tse\n\n";
    };

    my $__write_overflow = sub {
        my $self = shift;

        print "begin overflow\n";
        print $self->overflow() . "\n";
        print "end overflow\n\n";
    };

    my $__write_floating_ip = sub {
        my $self = shift;

        print "begin floating_ip\n";
        print $self->floating_ip() . "\n";
        print "end floating_ip\n\n";
    };

    my $__write_bdr = sub {
        my $self = shift;

        print "begin bdr\n";
        print $self->bdr() . "\n";
        print "end bdr\n\n";
    };


    open my $fh, '>', $filename or croak "Cannot write to file $filename";
    {
        local *STDOUT = $fh;

        $self->$__write_nodes();
        $self->$__write_enclosures();
        print "#============================\n"
            . "# Audit configuration\n"
            . "#============================\n";

        print "# Extents are for data audit volumes only, not \$TLOG\n"
            . "# Minimum is 60000\n"
            . "begin at_extent_size\n"
            . "60000\n"
            . "end at_extent_size\n\n";

        print "# Files per volume are for data audit volumes only, not \$TLOG\n"
            . "# Minimum is 40\n"
            . "begin at_files_per_volume\n"
            . "40\n"
            . "end at_files_per_volume\n\n";

        print "# Percentage of audit allowed before TX is DOOMED\n"
            . "# Maximum is 25\n"
            . "begin tx_capacity_threshold\n"
            . "25\n"
            . "end tx_capacity_threshold\n\n";

        print "#============================\n"
            . "# SQproxy Configuration\n"
            . "#============================\n"
            . "#\n"
            . "begin spproxy\n"
            . "end spproxy\n\n";

        $self->$__write_tmse();
        $self->$__write_ase();
        $self->$__write_tse();
        $self->$__write_overflow();
        $self->$__write_floating_ip();
        $self->$__write_bdr();
    }
    $fh->close();
    return 1;

}

1; # Magic true value required at end of module
